#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

/*
 Ces 2 fonctions sont utilisés pour avoir
 des captures sécurisées grâce à fgets()
 */

/*
 Fonction qui permet de "manger" les caractères restants car
 si la dernière saisie ne s'est pas déroulée correctement
 (ou on a dépassé le nombre de caractères max spécifié en deuxième argument)
 alors la seconde saisie va contenir les caractères qui n'ont pas été lus
 et ainsi de suite, jusqu'à ce que le flux stdin devienne vide.
 */
static void purger(void)
{
    int c;

    while ((c = getchar()) != '\n' && c != EOF)
        {}
}

/*
 Fonction qui permet de chercher le caractère '\n'
 et on le supprime (grâce au pointeur, on le déférence)
 */
static void clean (char *chaine)
{
    char *p = strchr(chaine, '\n');
    int c;

    if (p)
    {
        *p = 0;
    }

    else
    {
        purger();
    }
}

/*
 les 2 fonctions pour faire les tests sont les même,
 juste la différence se situe dans les fonctions
 d'ouverture, lecture et ériture utilisées
 */

/*
 Dans cette fonction, on a utilisé les fonctions standards
 du langage C (fopen, fprintf, fgets...)
 */
void test1(char *nomF)
{
    FILE *fp /* Le péripherique */,
         *fd /* Le fichier ( dans le cas où on a entré comme argument le nom du fichier)*/;

    /*
    Si on a entré un nom de fichier en paramètre, alors:
    - lecture du contenu du entré en paramètre et
    - écriture dans le péripherique
    */
    if (nomF!=NULL)
    {
        /****************** Ecriture ***********************/
        /*
        ouverture du périphérique en écriture
        */
        if ((fp = fopen("/dev/mblock","w")) == NULL)
        {
            perror("\nProblem in opening /dev/mblock\n");
            exit(1);
        }
        /*
        ouverture du fichier en lecture
        */
        if ((fd = fopen(nomF,"r")) == NULL)
        {
            /*
            Construction du message d'erreur
            pour l'afficher dans perror()
            */
            char *s1;
            char *s2="\nProblem in opening ";
            char *s3=nomF;
            s1 = (char *)calloc(strlen(s2) + strlen(s3) + 1, sizeof(char));
            strcat(s1,s2);
            strcat(s1,s3);
            perror(s1);
            exit(1);
        }

        int nbLigne=0; /* nombre de lignes du fichier */
        int ch;
        while ((ch=fgetc(fd)) !=EOF)
        {
            if (ch=='\n') nbLigne++;/* incrémenter le nombre de lignes */
            fputc(ch,fp); /* écrire le caractère ch dans le péripherique */
        }
        /* fermeture du péripherique et du fichier */
        fclose(fp);
        fclose(fd);

        /*********************** Lecture *******************/
        /*
        ouverture du périphérique en lecture
        */
        if ((fp = fopen("/dev/mblock","r")) == NULL)
        {
            perror("\nProblem in opening /dev/mblock\n");
            exit(1);
        }
        printf("<-----Contenu-de-/dev/mblock---------------->\n");
        /*
        tant qu'on a pas depassé le nombre de lignes,
        lire le contenu du péripherique caractère par caractère
        */
        while (nbLigne > 0)
        {
            ch=fgetc(fp);/* lire un caractère depuis le fichier et le mettra dans la variable ch */
            printf("%c",ch);
            if (ch=='\n') nbLigne--;/* décrementer le nombre de lignes */
        }
        printf("<--------------------------------------->\n");
        /* fermeture du péripherique */
        fclose(fp);
    }
    /*
    Sinon ( si on a pas entré d'argument à notre script),
    demander à l'utilisateur d'entrer une chaine, qui va être écrite dans le péripherique
    puis l'afficher depuis le péripherique
    */
    else
    {
        char chaine[255];/* chaine qui va contenir la saisie de l'utilisateur */
        /*********************** Ecriture *******************/
        /*
        ouverture du périphérique en écriture
        */
        if ((fp = fopen("/dev/mblock","w")) == NULL)
        {
            perror("\nProblem in opening /dev/mblock\n");
            exit(1);
        }
        /*
        prendre la saisie de l'utilisateur
        */
        printf("Veuillez entrer une phrase : ");
        fgets(chaine, sizeof chaine, stdin);
        /*
        "nettoyer" la saisie
        */
        clean(chaine);
        /*
        écrire la chaine dans le péripherique
        */
        fprintf(fp,chaine);
        /*
        fermeture du péripherique
        */
        fclose(fp);

        /*********************** Lecture *******************/
        /*
        ouverture du périphérique en lecture
        */
        if ((fp = fopen("/dev/mblock","r")) == NULL)
        {
            perror("\nProblem in opening /dev/mblock\n");
            exit(1);
        }
        char ch;
        int nbChar=0; /* nombre de caractères de la chaine */
        printf("/dev/mblock --> ");
        while (nbChar < strlen(chaine))
        {
            ch=fgetc(fp);/* lire un caractère depuis le fichier et le mettre dans la variable ch */
            printf("%c",ch);
            nbChar++; /* incrementer la valeur de la variable */
        }
        /*
        fermeture du périphérique
        */
        fclose(fp);
    }
}

/*
 Dans cette fonction, on a utilisé les fonction
 de plus bas niveau (open, read, write...)
 */
void test2(char *nomF)
{
    int mem_fd,fic;
    /*
    Si on a entré un nom de fichier en paramètre, alors:
    - lecture du contenu du entré en paramètre et
    - écriture dans le péripherique
    */
    if (nomF!=NULL)
    {
        /*********************** Ecriture *******************/
        /*
        ouverture du périphérique en lecture/écriture
        */
        if ((mem_fd = open("/dev/mblock",O_RDWR)) < 0 )
        {
            perror("\nProblem in opening /dev/mblock\n");
            exit(-1);
        }
        /*
        ouverture du fichier en lecture seulement
        */
        if ((fic = open(nomF,O_RDONLY)) < 0)
        {
            /*
            Construction du message d'erreur
            pour l'afficher dans perror()
            */
            char *s1;
            char *s2="\nProblem in opening ";
            char *s3=nomF;
            s1 = (char *)calloc(strlen(s2) + strlen(s3) + 1, sizeof(char));
            strcat(s1,s2);
            strcat(s1,s3);
            perror(s1);
            exit(-1);
        }
        int ch /* variable pour recevoir les caractères depuis le fichier*/,
            ligne=0;/* variable qui compte le nombre de lignes du fichier */
        /*
        lecture du contenu du fichier
        */
        while (read(fic, &ch, sizeof(char)) > 0)
        {
            if (ch=='\n') ligne++;/* incrementer la valeur de ligne */
            if (write(mem_fd, &ch, sizeof(ch)) < 0)/* écriture du caractère ch vers le péripherique */
            {
                perror("problem in wrinting to /dev/mblock");
                exit(-1);
            }
        }
        /*
        fermeture du fichier et du péripherique
        */
        close(fic);
        close(mem_fd);

        /*********************** Lecture *******************/
        /*
        ouverture du périphérique en lecture seulement
        */
        if ((mem_fd = open("/dev/mblock",O_RDONLY)) < 0)
        {
            perror("\n\nProblem in opening /dev/mblock");
            exit(-1);
        }
        /*
        tant qu'on a pas depassé le nombre de lignes,
        lire le contenu du péripherique caractère par caractère
        */
        printf("<-----Contenu-de-/dev/mblock---------------->\n");
        while (ligne > 0)
        {
            if (read(mem_fd, &ch, sizeof(ch))<0)
            {
                perror("\n\nProblem in reading from /dev/mblock");
            }
            printf("%c",ch);
            if (ch=='\n') ligne--;/* décrementer le nombre de lignes */
        }
        printf("<------------------------------------------->\n");
        /* fermeture du péripherique */
        close(mem_fd);

    }
    /*
    Sinon (si on a pas entré d'argument à notre script),
    demander à l'utilisateur d'entrer une chaine, qui va être écrite dans le péripherique
    puis l'afficher depuis le péripherique
    */
    else
    {
        char chaine[1024];/* chaine qui va contenir la saisie de l'utilisateur */
        /*********************** Ecriture *******************/
        /*
        ouverture du périphérique en lecture/écriture
        */
        if ((mem_fd = open("/dev/mblock",O_RDWR)) < 0)
        {
            perror("\n\nProblem in opening /dev/mblock");
            exit(-1);
        }
        /*
        prendre la saisie de l'utilisateur
        */
        printf("Veuillez entrer une phrase : ");
        fgets(chaine, sizeof chaine, stdin);
        /*
        "nettoyer" la saisie
        */
        clean(chaine);
        if (write (mem_fd, chaine, sizeof chaine) < 0)
        {
            perror("problem in wrinting to /dev/mblock");
            exit(-1);
        }
        /* fermeture du péripherique */
        close(mem_fd);

        /*********************** Lecture *******************/
        /*
        ouverture du périphérique en lecture seulement
        */
        if ((mem_fd = open("/dev/mblock",O_RDONLY)) < 0)
        {
            perror("\n\nProblem in opening /dev/mblock");
            exit(-1);
        }
        /*
        lecture de la chaine depuis le péripherique
        */
        if (read(mem_fd, chaine,sizeof(chaine) ) < 0)
        {
            perror("problem in reading from /dev/mblock");
            exit (1);
        }
        /* fermeture du péripherique */
        close(mem_fd);

        printf("Voici la chaine: %s\n",chaine);
    }
}

int main(int argc, char *argv[])
{
    test1(argv[1]);
    return 0;
}
